#include "triangle.h"
#include <QDateTime>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
GLuint VBO, VAO, lightVAO;

const char *vertexShaderSource =
        "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;"
        "layout (location = 1) in vec3 aNormal;"
        "layout (location = 2) in vec2 aTexCoords;"
        "out vec3 FragPos;"
        "out vec3 Normal;"
        "out vec2 TexCoords;"

        "uniform mat4 model;"
        "uniform mat4 view;"
        "uniform mat4 projection;"
        "void main()"
        "{"
        "   FragPos = vec3(model * vec4(aPos, 1.0));"
        "   gl_Position = projection * view * vec4(FragPos, 1.0);"
        "   Normal = mat3(transpose(inverse(model))) * aNormal;"
        "   TexCoords = aTexCoords;"
        "}";

const char *fragmentShaderSource =
        "#version 330 core\n"
        "struct Material {"
        "    sampler2D diffuse;" //漫反射光照材质向量定义了在漫反射光照下物体的颜色
        "    sampler2D specular;"  //镜面光照材质向量设置的是镜面光照对物体的颜色影响
        "    float shininess;" //反光度影响镜面高光的散射/半径
        "};"
        "struct Light {"
        "    vec3 position;"
        "    vec3 ambient;"
        "    vec3 diffuse;"
        "    vec3 specular;"
        "};"

        "out vec4 FragColor;"
        "in vec3 Normal;"
        "in vec3 FragPos;"
        "in vec2 TexCoords;"
        "uniform vec3 viewPos;"
        "uniform Material material;"
        "uniform Light light;"
        "void main(){"
        // 环境光
        "   vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;"

        // 漫反射
        "   vec3 norm = normalize(Normal);"
        "   vec3 lightDir = normalize(light.position - FragPos);"
        "   float diff = max(dot(norm, lightDir), 0.0);"
        "   vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));"

        // 镜面光
        "   vec3 viewDir = normalize(viewPos - FragPos);"
        "   vec3 reflectDir = reflect(-lightDir, norm);  "
        "   float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);"
        "   vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));"

        "   FragColor = vec4(ambient + diffuse + specular, 1.0);"
        "}";

const char *lightColorVs =
        "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;"   // 位置变量的属性位置值为 0
        "uniform mat4 model;"
        "uniform mat4 view;"
        "uniform mat4 projection;"

        "void main(){"
        "   gl_Position = projection * view * model * vec4(aPos, 1.0);"
        "}";

const char *lightColorFs =
        "#version 330 core\n"
        "out vec4 FragColor;"
        "void main()"
        "{"
        "   FragColor = vec4(1.0);"
        "}";

Triangle::Triangle(){

}

Triangle::~Triangle(){

}

void Triangle::initializeGL(){
    this->resize(1000,800);
    //着色器部分
    this->initializeOpenGLFunctions();
    // 监听键盘
    this->releaseKeyboard();
    // 开启深度测试
    glEnable(GL_DEPTH_TEST);

    shader = std::make_shared<Shader>(this);
    lightShader = std::make_shared<Shader>(this);

    shader->readShaderStr(vertexShaderSource,fragmentShaderSource);
    lightShader->readShaderStr(lightColorVs,lightColorFs);

    //VAO，VBO数据部分
    // ------------------------------------------------------------------
    float vertices[] = {
        // 位置          // 法向量           // 纹理位置
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
    };
    // 创建顶点数组对象
    glGenVertexArrays(1, &VAO);//两个参数，第一个为需要创建的缓存数量。第二个为用于存储单一ID或多个ID的GLuint变量或数组的地址
    // 绑定顶点缓冲区数组对象
    glBindVertexArray(VAO);

    // 创建顶点数组对象
    glGenBuffers(1, &VBO);
    // 绑定顶点缓冲区数组对象
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 立方体位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 点的法向量
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // 纹理位置
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);

    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);

    // 灯光位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // we only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need (it's already bound, but we do it again for educational purposes)
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    lightPos = glm::vec3(1.2f, 1.0f, 2.0f);

    // load textures (we now use a utility function to keep the code more organized)
    // -----------------------------------------------------------------------------
    // 纹理部分
    // 创建纹理
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    // 加载并生成纹理
    int width, height, nrChannels;
    unsigned char *data = stbi_load("D:\\Material\\container2.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        // JPG GL_RGB;PNG GL_RGBA
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        qDebug() << "Failed to load texture" << '\n';
    }
    stbi_image_free(data);
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    data = stbi_load("D:\\Material\\container2.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        // JPG GL_RGB;PNG GL_RGBA
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        qDebug() << "Failed to load texture" << '\n';
    }
    stbi_image_free(data);
    shader->use();
    shader->setInt("material.diffuse", 0);
    shader->setInt("material.specular", 1); // specular lighting doesn't have full effect on this object's material
    time.start();
}

void Triangle::resizeGL(int w, int h){
    QExpandOpenGLWidget::resizeGL(w, h);
}

void Triangle::paintGL(){
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 位置模型
    glm::mat4 model = glm::mat4(1.0f);
    view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);

    static float trasnCount = 0.0f;
    model = glm::translate(model, glm::vec3(0.5f));
    model = glm::scale(model, glm::vec3(0.5f));
    shader->use();
    shader->setMat4("model", glm::rotate(model, glm::radians(trasnCount*5), glm::vec3(1.0f, 0.3f, 0.5f)));
    shader->setVec3("viewPos", cameraPos);
    shader->setVec3("light.position", lightPos);
    shader->setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
    shader->setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
    shader->setVec3("light.specular", 1.0f, 1.0f, 1.0f);

    // material properties
    shader->setFloat("material.shininess", 64.0f);

    shader->setMat4("projection", projection);
    shader->setMat4("view", view);

    // bind diffuse map
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture1);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture2);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    // lighting
    lightShader->use();
    lightShader->setMat4("view", view);
    lightShader->setMat4("projection", projection);
    model = glm::translate(glm::mat4(1.0f), lightPos);
    model = glm::scale(model, glm::vec3(0.2f));
    lightShader->setMat4("model", model);

    glBindVertexArray(lightVAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    lightPos.x = 1.0f + sin(trasnCount) * 2.0f;
    lightPos.y = sin(trasnCount / 2.0f) * 1.0f;
    trasnCount+=0.05f;

    glUseProgram(0);
    this->update();
}

void Triangle::keyPressEvent(QKeyEvent *event){
    QExpandOpenGLWidget::keyPressEvent(event);
}

void Triangle::mouseReleaseEvent(QMouseEvent* event){
    QExpandOpenGLWidget::mouseReleaseEvent(event);
}


void Triangle::mousePressEvent(QMouseEvent *event) {
    QExpandOpenGLWidget::mousePressEvent(event);
}

void Triangle::mouseMoveEvent(QMouseEvent* event){
    QExpandOpenGLWidget::mouseMoveEvent(event);
}


void Triangle::wheelEvent(QWheelEvent *event) {
    QExpandOpenGLWidget::wheelEvent(event);
}
